#include <stdint.h>
#include <string.h>
#include "dev.h"
#include "flash.h"
#include "init.h"
#include "kernel.h"
#include "arch.h"
#include "syscfg.h"
#include "config.h"

#define __INST_microMIPS        1

#define EF_WR_WD                0x77776665
#define EF_RD_NVR               0x6e726665
#define EF_ER_SECTOR            0x73656665
#define FW_VER_STR              0x69767766

#define EF_BASE                 (0xB0000000)
#define EF_SECTOR_SIZE          2048
#define EF_SECTOR_SHIFT         11
#define EF_SECTOR_NR            94
#define EF_NVR1_SIZE            (EF_SECTOR_SIZE)
#define EF_NVR2_SIZE            EF_NVR1_SIZE

typedef uint32_t (*pef_get_api)(int);
typedef int (*pef_write_wd)(uint32_t offset, uint32_t wd);
typedef uint32_t (*pef_read_nvr)(uint32_t *outbuf, uint32_t offset, uint32_t size);
typedef int (*pef_erase_sector)(uint32_t sector);


static pef_write_wd ew;
static pef_read_nvr nvr_rd;
static pef_erase_sector ers;

typedef int (*api)();

int __attribute__((unused, __section__(".efinit"))) ef_initial(void)
{
        unsigned int volatile *pef = (unsigned int *)EF_BASE;
        volatile int i=10000;

        SYSCFG->RST_SOFT0 |= EFLASH_HRST;
        while(i--);
        SYSCFG->RST_SOFT0 &= ~EFLASH_HRST;

        for(i=0; i<8; i++)
        {
                pef[0x555] = 0xAA;
                pef[0xAAA] = 0x55;
                pef[0x555] = 0x51;
                pef[i] = 0;
        }
        return 0;
}

static inline void eflash_readnvr(uint32_t offset, void *buf, size_t len)
{
        nvr_rd(buf, (offset>>2), (len>>2));
}

#define EF_FAB_INFO_OFFSET			0x68
	#define GSMC_FAB1		1
#define EF_TEST_HOUSE_INFO_OFFSET	0x6c
	#define TEST_HOUSE_GSMC	1
	#define TEST_HOUSE_KYEC	2
	#define TEST_HOUSE_KLT	3
#define EF_TESTER_MODEL_OFFSET		0x70
	#define TESTER_MODEL_KALOS1	1
	#define TESTER_MODEL_KALOS2	2
#define EF_TESTER_CONFIG_OFFSET		0x74
	#define TESTER_CONFIG_SINGLE	1
	#define TESTER_CONFIG_8SITES	2
	#define TESTER_CONFIG_16SITES	3
	#define TESTER_CONFIG_64SITES	4
#define EF_CP_INFO_OFFSET			EF_FAB_INFO_OFFSET
typedef struct ef_cp_info {
	unsigned int fab_info;
	unsigned int test_house;
	unsigned int tester_model;
	unsigned int tester_config;
	unsigned int reserved[3];
	unsigned int lot_id;	// GSMC lot ID: A BCCCCC. DD
	unsigned int wafer_id;	// 0x0000,00AA
	unsigned int die_row_addr;	// 0x0000,AABB( Die row data)
	unsigned int die_col_addr;	// 0x0000CCDD (Die column data)
	unsigned int test_year;		// AABB (Year); CC (Month); DD (Day)
	unsigned int test_month;
	unsigned int test_day;
}st_cp_info;

#define EF_TEST_DATE_OFFSET		0x94
typedef struct ef_test_date {
	uint32_t year;
	uint32_t month;
	uint32_t day;
}st_ef_test_date;
static int ef_cp_ok(void)
{
	struct ef_test_date td;
	eflash_readnvr(EF_NVR2_SIZE+EF_TEST_DATE_OFFSET, &td, sizeof(struct ef_test_date));
	if((td.year<2012) || (td.month>12) || (td.day>31))
		return 0;	// invalid argument
	if((td.year==2012) && (td.month<9))
		return 0;	// cp failed.
	return 1;
}

static int flash_probe_eFlash(flash_t * fl)
{
        pef_get_api pf = (pef_get_api)(0xBFC00000 + 0x20 + __INST_microMIPS);
        ew = (pef_write_wd)pf(EF_WR_WD);
        nvr_rd = (pef_read_nvr)pf(EF_RD_NVR);
        ers = (pef_erase_sector)pf(EF_ER_SECTOR);
        fl->inf.nor_addr = EF_BASE;

        if(!ef_cp_ok())
        {
			volatile  api pc = (api)ef_initial;
			pc();
        }
        return 0;
}


static int eFlash_erase(flash_t * fl, uint32_t offset, size_t len)
{
        len = (len + (offset & 0x7FF) + EF_SECTOR_SIZE - 1) >> EF_SECTOR_SHIFT;
        offset >>= EF_SECTOR_SHIFT;
        while (len --) {
                __disable_interrupt();
                if (ers(offset ++) < 0) {
                        __enable_interrupt();
                        return -1;
                }
                __enable_interrupt();
        }
        return 0;
}
static int eFlash_read(flash_t * fl, uint32_t offset, void *buf, size_t len)
{
        memcpy(buf, (uint8_t *)(EF_BASE + offset), len); /* Uncached Reading */
        return len;
}

#define GET_LE_UINT32(src) \
                (*src) | ((*(src + 1)) << 8) | \
                ((*(src + 2)) << 16) | \
                ((*(src + 3)) << 24)
static int eFlash_write(flash_t * fl, uint32_t offset, const void *buf, size_t len)
{
        uint32_t wp;
        int cnt = len, tmp, of;
        uint32_t data;
        uint8_t * src = (uint8_t *)buf, tmpbuf[4];

        wp = (offset & ~0x03);
        data = 0;
        if ((of = offset - wp) != 0) {
                for (tmp = 0; tmp < of; tmp ++)
                        tmpbuf[tmp] = 0xFF;
                for (; tmp < 4 && tmp < len + of; tmp ++)
                        tmpbuf[tmp] = *(src + tmp - of);
                for (; tmp < 4; tmp ++)
                        tmpbuf[tmp] = 0xFF;
                data = GET_LE_UINT32(tmpbuf);
                src -= of;
                cnt += of;
        }
        else if (len > 3){
                data = GET_LE_UINT32(src);
        }
        else goto final;
        while(1) {
 tail:
                __disable_interrupt();
                if (ew(wp >> 2, data) < 0) {
                        __enable_interrupt();
                        return -1;
                }
                __enable_interrupt();

                cnt -= 4;
                src += 4;
                wp += 4;
                if (cnt < 4) break;
                data = GET_LE_UINT32(src);
       }
        if (cnt > 0) {
final:
                for (tmp = 0; tmp < cnt; tmp ++)
                        tmpbuf[tmp] = *(src + tmp);
                for (; tmp < 4; tmp ++)
                        tmpbuf[tmp] = 0xFF;
                data = GET_LE_UINT32(tmpbuf);
                goto tail;
        }
        return len;
}

static const flashdrv_t t1_eflash __flashdrv_entry= {
        .name = "eFlash",
        .probe = flash_probe_eFlash,
        .read = eFlash_read,
        .write = eFlash_write,
        .erase = eFlash_erase,
};

#ifdef CONFIG_USE_EFLASH_KEY
void read_vendor_pk(uint8_t * buf)
{
    __disable_interrupt();
    nvr_rd((uint32_t *)buf, 0, (256 >> 2));
    __enable_interrupt();
}
void get_dev_id(uint8_t * buf)
{
    __disable_interrupt();
    nvr_rd((uint32_t *)buf, (EF_NVR2_SIZE>>2)+0x21, (16 >> 2));
    __enable_interrupt();
}
#endif




